package cn.zcltd.btg.set.core;

import cn.zcltd.btg.set.ParamGetter;
import cn.zcltd.btg.set.adaptor.EventAdapter;
import cn.zcltd.btg.set.exception.BtgSetRuntimerException;
import cn.zcltd.btg.set.sourcemanager.SourceManager;
import cn.zcltd.btg.set.util.StringUtil;
import cn.zcltd.btg.sutil.EmptyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.*;

/**
 * 配置管理核心类
 */
public class ConfigManager implements ParamGetter {
    private final Logger log = LoggerFactory.getLogger(ConfigManager.class);
    private boolean devModel = false; //开发模式：当为开发模式时，会启用sourceManager缓存功能（前提是sourceManager支持缓存）
    private boolean inited = false; //是否初始化

    private Map<String, SourceManager> sourceManagerMap = new HashMap<>(); //配置管理器
    private SourceManager thisSourceManager; //当前配置资源管理器
    private Config thisConfig; //当前生效配置

    private List<EventAdapter> eventAdapters = new ArrayList<>();

    private static ConfigManager instance = new ConfigManager();

    private ConfigManager() {

    }

    public static ConfigManager getInstance() {
        return instance;
    }

    public boolean isDevModel() {
        return devModel;
    }

    public void setDevModel(boolean devModel) {
        this.devModel = devModel;
    }

    public boolean isInited() {
        return inited;
    }

    public Map<String, SourceManager> getSourceManagerMap() {
        return sourceManagerMap;
    }

    public void setSourceManagerMap(Map<String, SourceManager> sourceManagerMap) {
        this.sourceManagerMap = sourceManagerMap;
    }

    public SourceManager getSourceManager(String name) {
        if (EmptyUtil.isEmpty(name)) {
            throw new BtgSetRuntimerException("name must not be null");
        }
        return this.sourceManagerMap.get(name);
    }

    public void addSourceManager(SourceManager sourceManager) {
        if (null != this.sourceManagerMap.get(sourceManager.getName())) {
            throw new BtgSetRuntimerException(sourceManager.getName() + " already exists");
        }
        sourceManager.init(this.devModel);
        this.sourceManagerMap.put(sourceManager.getName(), sourceManager);
    }

    public SourceManager getThisSourceManager() {
        checkIsInited();
        return thisSourceManager;
    }

    public void setThisSourceManager(SourceManager thisSourceManager) {
        checkIsInited();
        if (null == thisSourceManager) {
            throw new BtgSetRuntimerException("thisSourceManager must not be null");
        }
        this.thisSourceManager = thisSourceManager;
    }

    public Config getThisConfig() {
        checkIsInited();
        checkIsDevMode();
        return thisConfig;
    }

    public void setThisConfig(Config thisConfig) {
        checkIsInited();
        if (null == thisConfig) {
            throw new BtgSetRuntimerException("thisConfig must not be null");
        }
        Config configOld = this.thisConfig.copy();
        this.thisConfig = thisConfig;
        fireEventOnThisConfigLoad(configOld, thisConfig);
        log.debug("change default config:" + thisConfig.getId());
    }

    public List<EventAdapter> getEventAdapters() {
        return eventAdapters;
    }

    public void setEventAdapters(List<EventAdapter> eventAdapters) {
        this.eventAdapters = eventAdapters;
    }

    public void addEventAdapter(EventAdapter eventAdapter) {
        if (null == eventAdapter) {
            throw new BtgSetRuntimerException("eventAdapter must not be null");
        }
        this.eventAdapters.add(eventAdapter);
    }

    /**
     * 初始化
     *
     * @param devModel             开发模式
     * @param defaultSourceManager 默认配置管理器
     * @param defaultConfigId      默认配置id
     */
    public void init(boolean devModel, SourceManager defaultSourceManager, String defaultConfigId) {
        log.debug("config manager init...");

        this.devModel = devModel;

        if (null == defaultSourceManager) {
            throw new BtgSetRuntimerException("defaultSourceManager must not be null");
        }
        this.sourceManagerMap.put(defaultSourceManager.getName(), defaultSourceManager);
        this.thisSourceManager = defaultSourceManager;
        thisSourceManager.init(devModel);

        Config config = this.thisSourceManager.use(defaultConfigId);
        if (null == config) {
            throw new BtgSetRuntimerException(defaultConfigId + " is not exists");
        }
        thisConfig = config;

        this.inited = true;

        log.debug("config manager init success.");
    }

    /**
     * 获取默认配置管理器
     *
     * @return 配置管理器
     */
    public SourceManager useSourceManager() {
        checkIsInited();
        return this.thisSourceManager;
    }

    /**
     * 获取指定名称配置管理器
     *
     * @param name name
     * @return 配置管理器
     */
    public SourceManager useSourceManager(String name) {
        if (EmptyUtil.isEmpty(name)) {
            throw new BtgSetRuntimerException("name must not be null");
        }
        SourceManager sourceManager = this.sourceManagerMap.get(name);
        if (null == sourceManager) {
            throw new BtgSetRuntimerException(name + " is not exists");
        }
        return sourceManager;
    }

    /**
     * 应用指定name的配置管理器
     *
     * @param name 配置管理器name
     */
    public void applySourceManager(String name) {
        checkIsInited();
        if (EmptyUtil.isEmpty(name)) {
            throw new BtgSetRuntimerException("sourceManagerName must not be null");
        }
        SourceManager sourceManager = this.sourceManagerMap.get(name);
        if (null == sourceManager) {
            throw new BtgSetRuntimerException("sourceManagerName not exists");
        }
        setThisSourceManager(sourceManager);
    }

    /**
     * 获取默认配置
     *
     * @return 配置
     */
    public Config use() {
        checkIsInited();
        checkIsDevMode();
        return thisConfig;
    }

    /**
     * 获取指定id的配置
     *
     * @param configId 配置id
     * @return 配置
     */
    public Config use(String configId) {
        checkIsInited();
        if (EmptyUtil.isEmpty(configId)) {
            throw new BtgSetRuntimerException("configId must not be null");
        }
        Config config = this.thisSourceManager.use(configId);
        if (null == config) {
            throw new BtgSetRuntimerException(configId + " is not exists");
        }
        return config;
    }

    /**
     * 应用指定id的配置
     *
     * @param configId 配置id
     */
    public void apply(String configId) {
        checkIsInited();
        if (EmptyUtil.isEmpty(configId)) {
            throw new BtgSetRuntimerException("configId must not be null");
        }
        Config config = this.thisSourceManager.use(configId);
        setThisConfig(config);
    }

    /**
     * 合并一个配置（存在则更新，不存在则新增，同时将配置加载到插件并保存到sourceManager）
     *
     * @param config 配置
     */
    public void merge(Config config) {
        checkIsInited();
        if (null == config) {
            throw new BtgSetRuntimerException("config must not be null");
        }
        this.thisSourceManager.save(config);
    }

    /**
     * 合并一个配置（存在则更新，不存在则新增，同时将配置加载到插件并保存到所有sourceManager）
     *
     * @param config 配置
     */
    public void mergeAll(Config config) {
        checkIsInited();
        if (null == config) {
            throw new BtgSetRuntimerException("config must not be null");
        }
        for (SourceManager sourceManager : sourceManagerMap.values()) {
            sourceManager.save(config);
        }
    }

    /**
     * 拷贝并重命名一个配置文件（配置文件id不能重名）
     *
     * @param configId 原始配置id
     * @param targetId 目标配置id
     * @return 拷贝得到的config
     */
    public Config copyTo(String configId, String targetId) {
        checkIsInited();
        if (EmptyUtil.isEmpty(configId)) {
            throw new BtgSetRuntimerException("configId must not be null");
        }
        if (EmptyUtil.isEmpty(targetId)) {
            throw new BtgSetRuntimerException("targetId must not be null");
        }
        if (targetId.equals(configId)) {
            throw new BtgSetRuntimerException("targetId cannot be the same:" + configId);
        }

        Config config = use(configId);
        if (null == config) {
            throw new BtgSetRuntimerException(configId + " is not exists");
        }

        //copy
        Config configCopy = config.copy();
        configCopy.setId(targetId);

        //add to sourceManger
        this.thisSourceManager.save(configCopy);

        return configCopy;
    }

    /**
     * 将sourceManager的配置同步到targetSourceManager
     *
     * @param sourceManager       原始渠道
     * @param targetSourceManager 目标渠道
     */
    public void syn(SourceManager sourceManager, SourceManager targetSourceManager) {
        checkIsInited();
        if (null == sourceManager) {
            throw new BtgSetRuntimerException("sourceManager must not be null");
        }
        if (null == targetSourceManager) {
            throw new BtgSetRuntimerException("targetSourceManager must not be null");
        }
        List<Config> configs = sourceManager.useAll();
        for (Config config : configs) {
            targetSourceManager.save(config);
        }
    }

    /**
     * 将当前sourceManager的配置同步到targetSourceManager
     *
     * @param targetSourceManager 目标渠道
     */
    public void syn(SourceManager targetSourceManager) {
        this.syn(thisSourceManager, targetSourceManager);
    }

    @Override
    public String getString(String key) {
        return getString(key, null);
    }

    @Override
    public String getString(String key, String defaultValue) {
        Param param = getThisConfig().getParam(key);
        return ParamConvert.getString(param, defaultValue);
    }

    @Override
    public Boolean getBoolean(String key) {
        return getBoolean(key, null);
    }

    @Override
    public Boolean getBoolean(String key, Boolean defaultValue) {
        Param param = getThisConfig().getParam(key);
        return ParamConvert.getBoolean(param, defaultValue);
    }

    @Override
    public Number getNumber(String key) {
        return getNumber(key, null);
    }

    @Override
    public Number getNumber(String key, Number defaultValue) {
        Param param = getThisConfig().getParam(key);
        return ParamConvert.getNumber(param, defaultValue);
    }

    @Override
    public Integer getInteger(String key) {
        return getInteger(key, null);
    }

    @Override
    public Integer getInteger(String key, Integer defaultValue) {
        Param param = getThisConfig().getParam(key);
        return ParamConvert.getInteger(param, defaultValue);
    }

    @Override
    public Long getLong(String key) {
        return getLong(key, null);
    }

    @Override
    public Long getLong(String key, Long defaultValue) {
        Param param = getThisConfig().getParam(key);
        return ParamConvert.getLong(param, defaultValue);
    }

    @Override
    public Float getFloat(String key) {
        return getFloat(key, null);
    }

    @Override
    public Float getFloat(String key, Float defaultValue) {
        Param param = getThisConfig().getParam(key);
        return ParamConvert.getFloat(param, defaultValue);
    }

    @Override
    public Double getDouble(String key) {
        return getDouble(key, null);
    }

    @Override
    public Double getDouble(String key, Double defaultValue) {
        Param param = getThisConfig().getParam(key);
        return ParamConvert.getDouble(param, defaultValue);
    }

    @Override
    public BigDecimal getBigDecimal(String key) {
        return getBigDecimal(key, null);
    }

    @Override
    public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
        Param param = getThisConfig().getParam(key);
        return ParamConvert.getBigDecimal(param, defaultValue);
    }

    @Override
    public Date getDate(String key) {
        return getDate(key, "");
    }

    @Override
    public Date getDate(String key, Date defaultValue) {
        return getDate(key, "", defaultValue);
    }

    @Override
    public Date getDate(String key, String datePattern) {
        return getDate(key, datePattern, null);
    }

    @Override
    public Date getDate(String key, String datePattern, Date defaultValue) {
        Param param = getThisConfig().getParam(key);
        return ParamConvert.getDate(param, key, defaultValue);
    }

    private void checkIsInited() {
        if (!inited) {
            throw new BtgSetRuntimerException("not inited");
        }
    }

    private void checkIsDevMode() {
        if (devModel) {
            setThisConfig(this.thisSourceManager.load(this.thisConfig.getId()));
        }
    }

    private void fireEventOnThisConfigLoad(Config configOld, Config configNew) {
        for (EventAdapter eventAdapter : this.eventAdapters) {
            eventAdapter.onThisConfigLoad(configOld, configNew);
        }
    }
}